namespace HZY.Framework.DependencyInjection.Attributes;

/// <summary>
/// 属性注入实例
/// </summary>
[AttributeUsage(
    AttributeTargets.Property |
    AttributeTargets.Method |
    AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)]
public class AutowiredAttribute : AopMoAttribute
{
    public override void OnEntry(MethodContext context)
    {
        base.OnEntry(context);

        // 判断当前进入函数是否为构造函数 ， 如果是构造中，需要检测是否有 IServiceProvider
        if (context.Method is ConstructorInfo)
        {
            IServiceProvider? serviceProvider = null;

            foreach (var item in context.Arguments)
            {
                if (item is IServiceProvider service)
                {
                    serviceProvider = service;
                }
            }

            if (serviceProvider is null)
            {
                throw new ArgumentNullException(" 构造函数中未找到 IServiceProvider " + nameof(serviceProvider));
            }

            if (context.Target is not IAopServiceProvider aopServiceProvider)
            {
                throw new ArgumentNullException(" 请继承 IAopServiceProvider 接口 ");
            }

            aopServiceProvider.ServiceProvider = serviceProvider;
        }
    }

    public override void OnSuccess(MethodContext context)
    {
        if (context.ReturnType == null) return;

        var methodInfo = GetMethodInfo(context.Target?.GetType(), context.Method.Name);
        if (methodInfo is null) return;

        // 获取返回值类型
        var service = GetService(context, methodInfo.ReturnType);

        if (service is not null)
        {
            context.ReplaceReturnValue(this, service);
            return;
        }

        Type type = context.ReturnType;

        service = this.GetService(context, type);
        if (service is null) return;

        context.ReplaceReturnValue(this, service!);
    }

    /// <summary>
    /// 获取函数信息
    /// </summary>
    /// <param name="type"></param>
    /// <param name="methodName"></param>
    /// <returns></returns>
    private static MethodInfo? GetMethodInfo(Type? type, string methodName)
    {
        while (true)
        {
            if (type is null)
            {
                return default;
            }

            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
                                       BindingFlags.Static;
            var methodInfo = type.GetMethod(methodName, flags);

            if (methodInfo is not null)
            {
                return methodInfo;
            }

            type = type.BaseType;
        }
    }
}